<?php

namespace App\Services;

use App\Exceptions\ApiException;
use App\Models\Classify;
use App\Models\PartCompany;
use App\Models\PartCompanyClassify;
use App\Models\Project;
use App\Models\ProjectClassify;
use App\Models\SortLink;
use App\Models\SortLinkSearch;
use Illuminate\Support\Facades\DB;
use Illuminate\Support\Facades\Schema;
use PhpOffice\PhpSpreadsheet\Calculation\LookupRef\Sort;
use PHPUnit\Util\Exception;

/**
 * 分类目录服务层
 * Class ClassifyService
 * @package App\Services
 */
class ProjectClassifyService extends BaseService
{
    protected $projectService;

    public function __construct()
    {
        parent::__construct();
        $this->projectService = new ProjectService();
    }

    /**
     * 判断是否有下级
     */
    public static function isHaveChild($projectClassifyId)
    {
        return ProjectClassify::query()->where('parent_id', $projectClassifyId)->first();
    }

    /**
     * 参建单位新建分类目录
     * 将classify里的数据 复制到 project_classify
     */
    public function addNewData(array $params)
    {
        DB::beginTransaction();
        try {
            $archivesNo                = format_archives_no($params['archives_no']);
            $title                     = format_str($params['title']);
            $partCompanyType           = PartCompany::query()->where('id', $params['part_company_id'])->value('type');
            $archivesNoData            = self::sliceArchivesNo($archivesNo);
            $data                      = [];
            $data['title']             = $title;
            $data['parent_id']         = to_int($params['parent_id']);
            $data['template_id']       = 0;
            $data['archives_no']       = $archivesNo; //档案号
            $data['type']              = $params['type'] ?? '';  //J或K
            $data['project_no']        = $archivesNoData['project_no']; //项目代码
            $data['stage_code']        = to_int($params['stage_code']);
            $data['major_code']        = $params['major_code'] ?? '';
            $data['creator_id']        = user_id();
            $data['part_company_type'] = $partCompanyType;
            $data['part_company_id']   = to_int($params['part_company_id']); //参建单位id
            $data['add_direct']        = 1; //0不可直接添加，1可以
            $data['is_can_edit']       = 1; //0不可以，1可以编辑
            $data['created_at']        = date('Y-m-d H:i:s'); //创建时间

            $classify            = Classify::query()->create($data);
            $data['classify_id'] = $classify->id;

            $classify->delete(); //创建之后，获取主键再删除

            ProjectClassify::query() //如果这个参建单位下之前有这个档号的数据都删除，防止导入的案卷数据匹配错误
            ->where('part_company_id', $params['part_company_id'])
                ->where('title', $title)
                ->where('archives_no', $archivesNo)
                ->delete();

            $projectId          = PartCompany::query()->where('id', $params['part_company_id'])->value('project_id');
            $data['project_id'] = $projectId;
            $projectClassifyId  = ProjectClassify::query()->insertGetId($data); //新增一条数据
            PartCompanyClassify::query()->insert([
                'project_id'          => $projectId,
                'part_company_id'     => $params['part_company_id'],
                'project_classify_id' => $projectClassifyId,
                'created_at'          => now(),
            ]);
            CommonService::addActionLog('添加项目分类', "数据ID:{$classify->id}; 名称:{$params['title']}");
            DB::commit();
        } catch (Exception $exception) {
            DB::rollBack();
            ErrorMsgService::write($exception, request()->url());
        }
    }

    /**
     * 编辑数据
     *
     * @param $id
     * @param $params
     * @return bool
     * @throws ApiException
     * @author: lxk
     * @date: 2023-05-12 15:29
     */
    public function updateData($id, $params): bool
    {
        $projectClassify = ProjectClassify::query()->find($id);
        if (empty($projectClassify)) {
            throw new ApiException('数据不存在！');
        }
        $archivesNo = format_archives_no($params['archives_no']);

        $projectClassify->title            = $params['title'];
        $projectClassify->type             = $params['type'];
        $projectClassify->archives_no      = $archivesNo;
        $projectClassify->project_no       = format_str($params['project_no']);
        $projectClassify->stage_code       = to_int($params['stage_code']);
        $projectClassify->major_code       = $params['major_code'];
        $projectClassify->early_stage_code = $params['early_stage_code'];
        $projectClassify->add_direct       = 1; //0不可直接添加，1可以

        CommonService::addActionLog('编辑项目分类', "数据ID:{$id}");

        return $projectClassify->save();
    }

    /**
     * 删除当前参见单位下相同档号的一条数据
     * 防止导入的案卷数据匹配错误
     * @author: lxk
     * @date: 2024-10-10 11:37
     */
    public static function delSameArchivesNoData($partCompanyId, $archivesNo)
    {
        ProjectClassify::query()->where('part_company_id', $partCompanyId)->where('archives_no', $archivesNo)->delete();
    }

    /**
     * 返回参建单位下的树状图
     * 获取参建单位类型 类型必须是1-3之间
     * 项目匹配完分类模板之后，添加的参见单位其实有默认项目分类，即匹配施工单位哪一步创建的
     * 思路：首先判断 part_company_classify 表是否已经同步数据，有的话就按照这个表里面的
     * @param $partCompanyId
     * @return array[]
     * @author: lxk
     * @date: 2024/3/19 13:43
     */
    public function treeClassifies($partCompanyId): array
    {
        $partCompany         = PartCompany::query()->find($partCompanyId);
        $templateId          = Project::query()->where('id', $partCompany->project_id)->value('template_id');
        $partCompanyType     = $partCompany->type; //参见单位的类型
        $partCompanyClassify = PartCompanyClassify::query()->where('part_company_id', $partCompanyId)->first();

        $baseQuery = ProjectClassify::query();
        if (!empty($partCompanyClassify)) {
            $projectClassifyIds = PartCompanyClassify::query()->where('part_company_id', $partCompanyId)->pluck('project_classify_id')->toArray();
            $projectClassifies  = $baseQuery->whereIn('id', $projectClassifyIds)->get();
            $list               = $projectClassifies->isNotEmpty() ? $projectClassifies->toArray() : [];
        } else {
            $projectClassifies = $baseQuery //获取分类目录
                ->where('template_id', $templateId)
                ->where('part_company_type', $partCompanyType) //基础项目分类
                ->orWhere('part_company_id', $partCompanyId) //参见单位后期自己加的
                ->get();
            $list = $projectClassifies->isNotEmpty() ? $projectClassifies->toArray() : [];
            /**
             * 首次查出来之后在关联表同步一下数据，下次去关联表查找
             */
            $projectClassifyIds = array_column($list, 'id');
            $insertData         = [];
            foreach ($projectClassifyIds as $classifyId) { //构造数组，追加额外数据
                $insertData[$classifyId] = [
                    'project_id' => $partCompany->project_id,
                    'created_at' => now(),
                ];
            }
            $partCompany->classify()->sync($insertData); //创建了参建单位和项目分类之间的关系，因为不同的参建用的项目分类不一样
        }
        $partCompany->save();
        $project = ProjectService::getInfo($partCompany->project_id);
        foreach ($list as &$val) { //项目代码填充替换
            $val['project_no']     = $project->document_number;
            $val['archives_no']    = str_replace('[项目代码]', $project->document_number, $val['archives_no']);
            $val['data_count']     = CommonService::getBookTitleSearchInstance($partCompanyId, $val['id'])->count(); //单位工程案卷总数
            $val['is_show_action'] = 0;
        }
        $redisKey = self::getProjectClassifyKey($partCompanyId);
        redis()->setex($redisKey, 3600 * 24 * 3, json_encode($list)); //用于可能导入项目分类用
        $data = self::getProjectClassifyData($partCompanyId, $list, 0); //注意！这个 classify_id 在 project_classifies 中相当于 parent_id

        $totalDataCount = array_sum(array_column($list, 'data_count')); //全部案卷总数
        $firstNode      = [
            'id'          => -1,
            'title'       => '全部案卷',
            'archives_no' => PartCompany::query()->where('id', $partCompanyId)->value('title'),
            'data_count'  => $totalDataCount,
        ];
        array_unshift($data, $firstNode); //数组开头插入一个值
        $info = SortLink::query()->where('part_company_id', $partCompanyId)->first();
        if (empty($info)) {
            $this->saveUnitProjectSort($partCompanyId, $data);
        }
        $this->delDiscardTable($partCompanyId); //把已删除的单位工程对应的表也删除

        return $data;
    }

    /**
     * 标记废弃的单位工程和对应的数据表，以免统计的数据有偏差
     * 因为起初设计不完善，有的单位工程被删除，他的关联下级没被删，导致统计的时候计算了对应的数据表，把这个方法加上，就修正了原来的数据缺陷
     *
     * @param $partCompanyId
     * @param $list
     * @author: lxk
     * @date: 2024-11-20 13:37
     */
    public function delDiscardTable($partCompanyId)
    {
        $projectClassifyIds = SortLink::query()->where('part_company_id', $partCompanyId)->pluck('project_classify_id')->toArray(); //当前单位工程树的数据
        ProjectClassify::query()
            ->where('part_company_id', $partCompanyId)
            ->whereNotIn('id', $projectClassifyIds)
            ->delete();
        $delProClassifyIds = ProjectClassify::onlyTrashed()->pluck('id'); //获取已被删除的单位工程
        $delProClassifyIds = $delProClassifyIds->isNotEmpty() ? $delProClassifyIds->toArray() : [];
        foreach ($delProClassifyIds as $projectClassifyId) {
            $table = "book_titles_{$partCompanyId}_{$projectClassifyId}";
            if (Schema::hasTable($table)) {
                Schema::drop($table); //把已删除的单位工程对应的表也删除
            }
        }
    }

    /**
     * 把单位工程树状图按照顺序写进表里面，先这样写吧，简单些
     *
     * @param $partCompanyId
     * @param $data
     * @author: lxk
     * @date: 2024/5/23 10:10
     */
    public function saveUnitProjectSort($partCompanyId, $data)
    {
        $projectClassifyIds = [];
        foreach ($data as $val) {
            $projectClassifyIds[] = $val['id'];
            if (isset($val['children'])) {
                foreach ($val['children'] as $val2) {
                    $projectClassifyIds[] = $val2['id'];
                    if (isset($val2['children'])) {
                        foreach ($val2['children'] as $val3) {
                            $projectClassifyIds[] = $val3['id'];
                            if (isset($val3['children'])) {
                                foreach ($val['children'] as $val4) {
                                    $projectClassifyIds[] = $val4['id'];
                                    if (isset($val4['children'])) {
                                        foreach ($val4['children'] as $val5) {
                                            $projectClassifyIds[] = $val5['id'];
                                        }
                                    }
                                }
                            }
                        }
                    }
                }
            }
        }
        $projectClassifyIds = array_unique($projectClassifyIds);
        $insertData         = [];
        foreach ($projectClassifyIds as $projectClassifyId) {
            $insertData[$projectClassifyId] = [
                'part_company_id'     => $partCompanyId,
                'project_classify_id' => $projectClassifyId,
            ];
        }
        $partCompany = new PartCompany();
        $partCompany->projectClassifyId()->sync($insertData); //中间表数据写入
    }


    /**
     * 获取项目分类树
     *
     * @param $list
     * @param $id
     * @return array
     * @author: lxk
     * @date: 2024/3/19 13:46
     */
    public static function getProjectClassifyData($partCompanyId, $list, $id): array
    {
        $tree = [];
        foreach ($list as $value) {
            $value['have_children'] = 0;
            if ($value['parent_id'] == $id) {
                $value['children'] = self::getProjectClassifyData($partCompanyId, $list, $value['classify_id']);
                if ($value['children']) {
                    $children = $value['children'];
                    if ($partCompanyId) {
                        $redisKey  = "childrenBts:{$partCompanyId}:{$value['id']}";
                        $selfTable = "book_titles_{$partCompanyId}_{$value['id']}";
                        redis()->sadd($redisKey, $selfTable); //存本身
                        foreach ($children as $value2) {
                            $childTable = "book_titles_{$partCompanyId}_{$value2['id']}";
                            redis()->sadd($redisKey, $childTable); //存本身
                            if ($value2['have_children']) {
                                foreach ($value2['children'] as $value3) {
                                    $childTable = "book_titles_{$partCompanyId}_{$value3['id']}";
                                    redis()->sadd($redisKey, $childTable); //存本身
                                    if ($value3['have_children']) {
                                        foreach ($value3['children'] as $value4) {
                                            $childTable = "book_titles_{$partCompanyId}_{$value4['id']}";
                                            redis()->sadd($redisKey, $childTable); //存本身
                                            if ($value4['have_children']) {
                                                foreach ($value4['have_children'] as $value5) {
                                                    $childTable = "book_titles_{$partCompanyId}_{$value5['id']}";
                                                    redis()->sadd($redisKey, $childTable); //存本身
                                                }
                                            }
                                        }
                                    }
                                }
                            }
                        }
                        redis()->expire($redisKey, 3600 * 24 * 3); //设置有效期3天
                    }
                    $value['have_children'] = 1; //下面，因为有的单位工程本身有值（原则上本身有下级的单位工程不要传案卷，但是个别还是传了），所以也要加上
                    $value['data_count']    = $value['data_count'] + array_sum(array_column($value['children'], 'data_count'));
                } else {
                    unset($value['children']);
                }
                $tree[] = $value;
            }
        }

        return $tree;
    }


    /**
     * 获取项目分类的id
     */
    public static function getProjectClassifyArrInfo($partCompanyId, $archivesNo, $allImportArchiveNosRedisKey = null, $canImportArchiveNosRedisKey = null)
    {
        $info                    = [];
        $projectClassifyRedisKey = ProjectClassifyService::getProjectClassifyKey($partCompanyId); //这里面存了项目分类数组
        $list                    = json_decode(redis()->get($projectClassifyRedisKey), true);
        foreach ($list as $projectClassifyInfo) {
            $mainArchivesNo = substr($archivesNo, 0, strrpos($archivesNo, '-'));
            $allImportArchiveNosRedisKey && redis()->sadd($allImportArchiveNosRedisKey, $mainArchivesNo); //全部
            if ($mainArchivesNo == format_archives_no($projectClassifyInfo['archives_no'])) {
                $info = $projectClassifyInfo;
                $canImportArchiveNosRedisKey && redis()->sadd($canImportArchiveNosRedisKey, $mainArchivesNo); // OK
                break;
            }
        }

        return $info;
    }

    /**
     * 获取一个临时存储该参建单位项目分类树状图的键
     *
     * @param $partCompanyId
     * @return string
     * @author: lxk
     * @date: 2024/3/19 14:13
     */
    public static function getProjectClassifyKey($partCompanyId): string
    {
        return $partCompanyId . user_id() . get_ip_num();
    }

    /**
     * 删除项目分类 清空分类下面的全部数据
     * 其实删除的是参建单位下的分类，因为有的参建单位不需要某些分类，比如，干机电工程的压根儿用不到土建分类
     */
    public function delProjectClassify($partCompanyId, $projectClassifyId)
    {
        PartCompanyClassify::query()
            ->where('part_company_id', $partCompanyId)
            ->where('project_classify_id', $projectClassifyId)
            ->delete();
        $info = ProjectClassify::query()->find($projectClassifyId);
        if ($info && $info->part_company_id > 0) { //如果删除的是后期添加的项目分类
            $info->delete();
            ProjectClassify::query() //如果有下级，把下级也删除
            ->where('part_company_id', $partCompanyId)
                ->where('parent_id', $info->classify_id)
                ->delete();
        }
        //清空分类下面的全部数据
        Schema::dropIfExists("book_titles_{$partCompanyId}_{$projectClassifyId}");
        Schema::dropIfExists("book_catalogs_{$partCompanyId}_{$projectClassifyId}");
        //如果有PDF,把PDF从OSS删除
        if (CommonService::isDelOssObject()) { //正式服操作才删除PDF
            $pdfTable = "book_catalog_pdfs_{$partCompanyId}_{$projectClassifyId}";
            if (Schema::hasTable($pdfTable)) { //如果分表存在
                $pdfInstance = $this->getBookCatalogPdfInstance($partCompanyId, $projectClassifyId);
                if ($pdfInstance->first()) { //如果有pdf数据
                    $fileUrls   = $pdfInstance->pluck('file_url')->toArray();
                    $attachment = new AttachmentService();
                    foreach ($fileUrls as $fileUrl) {
                        $attachment->delOssFile($fileUrl); //删除pdf
                    }
                }
            }
        }
        Schema::dropIfExists("book_catalog_pdfs_{$partCompanyId}_{$projectClassifyId}");

        return true;
    }

    /**
     * 获取参见单位的分类目录信息
     */
    public static function getProjectClassifiesInfo($id, $projectId)
    {
        $project = ProjectService::getInfo($projectId);
        $info    = ProjectClassify::query()->where('id', $id)->first()->toArray();

        $base = $info['type'] . '•' . $info['project_no'] . '•' . $info['stage_code'] . $info['major_code'] . $info['early_stage_code'];

        $info['document_number'] = $project->document_number;
        $info['archives_no']     = str_replace('[项目代码]', $project->document_number, $info['archives_no']);
        $info['other_char']      = str_replace($base, '', $info['archives_no']);
        $info['stage_code']      = to_int($info['stage_code']);


        return $info;
    }

    /**
     * 获取参建单位下面的全部项目分类
     *
     * @param $partCompanyId
     * @return array
     * @author: lxk
     * @date: 2025-01-12 14:09
     */
    public static function getProjectClassifyIds($partCompanyId): array
    {
        return PartCompanyClassify::query()->distinct()
            ->where('part_company_id', $partCompanyId)
            ->orderBy('project_classify_id')
            ->pluck('project_classify_id')
            ->toArray();
    }

    /**
     * 根据完整的档案号获取
     *
     * type ： 种类 J或K
     * project_no : 项目代码
     * stage_code : 阶段代码
     * major_code : 专业代码
     * 例如：J·SJKZ·5XL2(56/85)
     */
    public static function sliceArchivesNo($archivesNo)
    {
        $archivesNo = format_archives_no($archivesNo);

        $pos1 = strpos($archivesNo, '•'); //第1个•出现的位置
        $type = substr($archivesNo, 0, 1); //得到种类（J或K）

        $archivesNo2 = substr($archivesNo, $pos1 + 3); //把用过的截取掉，剩下 SJKZ·5XL2(56/85)
        $pos2        = strpos($archivesNo2, '•'); //第2个•出现的位置
        $projectNo   = substr($archivesNo2, 0, $pos2); //得到项目代码

        $archivesNo3 = substr($archivesNo2, $pos2 + 3); //5XL2(56/85)
        $stageCode   = substr($archivesNo3, 0, 1); //得到阶段代码

        $majorCode = '';
        if (!is_numeric($archivesNo3)) { //排除 J·SJKZ·5 这种
            $archivesNo4 = substr($archivesNo3, 1); //把用过的截取掉，剩下 XL2(56/85)
            $majorCode   = substr($archivesNo4, 0, 2); //先获取两个字符
            if (!in_array($majorCode, Classify::VUE_USED_ZY)) {
                $majorCode = substr($archivesNo4, 0, 1); //获取一个字符的情况
                if (!in_array($majorCode, Classify::VUE_USED_ZY)) {
                    //throw new ApiException('未知专业代码:'.$majorCode);
                    info('包含未知专业代码:' . $archivesNo);
                }
            }
        }
        return [
            'type'       => $type,
            'project_no' => $projectNo,
            'stage_code' => $stageCode,
            'major_code' => $majorCode,
        ];
    }

    /**
     * 删除后，获取单位工程树的时候，会重新生成一批新的关联数据
     *
     * @param $partCompanyId
     * @author: lxk
     * @date: 2024/5/23 9:35
     */
    public function delSortLinkData($partCompanyId)
    {
        SortLink::query()->where('part_company_id', $partCompanyId)->delete();
        $projectId = PartCompany::query()->where('id', $partCompanyId)->value('project_id');
        SortLinkSearch::query()->where('project_id', $projectId)->delete();
    }

}
